#ifndef __GOBANG_SERVER_HPP__
#define __GOBANG_SERVER_HPP__
#include "session.hpp"
#include "util.hpp"
#include "matching.hpp"
#include "dp.hpp"
#include "conn_ctl.hpp"

class gobang_server
{
private:
    user_table _table;
    conn_ctl _conn;
    room_ctl _room_ctl;
    server _ser;
    session_ctl _session_ctl;
    match_ctl _match_ctl;
    std::string _web_path = "./wwwroot/";
    std::string _404_path = "./wwwroot/404.html";

public:
    gobang_server(const std::string &host,
                  const std::string &user,
                  const std::string &password,
                  const std::string &db,
                  const uint16_t &port) : _table(host, user, password, db, port),
                                          _room_ctl(&_table, &_conn),
                                          _session_ctl(&_ser),
                                          _match_ctl(&_table, &_room_ctl, &_conn)

    {
        _ser.set_access_channels(websocketpp::log::alevel::none);
        _ser.init_asio();          // 初始化asio调度器
        _ser.set_reuse_addr(true); // 设置端口复用

        // 绑定任务接口
        _ser.set_http_handler(std::bind(&gobang_server::http_hander, this, std::placeholders::_1));
        _ser.set_open_handler(std::bind(&gobang_server::web_open_hander, this, std::placeholders::_1));
        _ser.set_close_handler(std::bind(&gobang_server::web_close_hander, this, std::placeholders::_1));
        _ser.set_message_handler(std::bind(&gobang_server::web_run_hander, this, std::placeholders::_1, std::placeholders::_2));
    }

    ~gobang_server() {}

    void run(uint16_t port) // 启动服务器
    {
        _ser.listen(port);   // 设置监听端口
        _ser.start_accept(); // 开始接受套接字
        _ser.run();          // 启动服务器
    }

private:
    void ret_file(server::connection_ptr &ptr) // 静态资源返回函数
    {
        websocketpp::http::parser::request req = ptr->get_request();
        std::string uri = req.get_uri();
        uri = _web_path + uri;
        // 判断是文件还是文件夹
        if (uri.back() == '/') // 代表请求了根目录 '/'
        {                      // 返回登录页面
            uri = _web_path + "login.html";
        }
        // 打开文件
        std::string file_body;
        bool ret = read_file_util::read_file(uri, file_body);
        if (!ret)
        {
            // 读取失败,返回 404 页面
            read_file_util::read_file(_404_path, file_body);

            ptr->set_body(file_body);
            ptr->set_status(websocketpp::http::status_code::not_found);
            // 大小和类型都会自动识别和添加上去,但是规范一下自己添加
            ptr->append_header("Content-Type", "text/html");
            return;
        }
        // 读取成功，返回内容
        ptr->set_status(websocketpp::http::status_code::ok);
        ptr->set_body(file_body);
        return;
    }

    // 填写http返回报文
    void http_resp(server::connection_ptr &ptr, bool result, websocketpp::http::status_code::value code, const std::string &reason)
    {
        Json::Value ret;
        ret["result"] = result;
        ret["reason"] = reason;
        // 序列化设置成报文主题

        std::string ret_str;
        json_util::serialize(ret, ret_str);
        ptr->set_status(code);
        ptr->set_body(ret_str);
        ptr->append_header("Content-Type", "application/json");
    }

    void user_reg(server::connection_ptr &ptr) // 用户注册页面
    {
        // 拿到正文 json串后进行反序列化
        std::string body = ptr->get_request_body();
        Json::Value user;
        json_util::unserialize(user, body); // 不考虑失败,概率太低

        if (user["username"].isNull() || user["password"].isNull())
        {
            debug_log("用户账号密码不完整");
            return http_resp(ptr, false, websocketpp::http::status_code::bad_request, "请填写用户名或者密码");
        }

        std::string username = user["username"].asCString();
        std::string password = user["password"].asCString();
        bool insert_ret = _table.insert(user);
        if (!insert_ret)
        { // 插入失败
            debug_log("用户插入失败, user:: %s", username.c_str());
            return http_resp(ptr, false, websocketpp::http::status_code::bad_request, "用户已经存在");
        }

        // 插入成功
        return http_resp(ptr, true, websocketpp::http::status_code::ok, "注册成功");
    }

    void user_login(server::connection_ptr &ptr)
    {
        // 拿到正文 json串后进行反序列化
        std::string body = ptr->get_request_body();
        Json::Value user;
        json_util::unserialize(user, body); // 不考虑失败,概率太低

        if (user["username"].isNull() || user["password"].isNull())
        {
            debug_log("用户账号密码不完整");
            return http_resp(ptr, false, websocketpp::http::status_code::bad_request, "请填写用户名或者密码");
        }

        std::string username = user["username"].asCString();
        std::string password = user["password"].asCString();

        // 开始登录
        bool ret = _table.login(user);
        if (!ret)
        { // 失败可能有很多种，后端都可以看到，前端统一返回用户已经存在
            return http_resp(ptr, false, websocketpp::http::status_code::bad_request, "用户账号密码错误");
        }

        // 登录成功创建 session，并设定过期时间
        int uid = user["id"].asInt();
        session_ptr session_tmp = _session_ctl.create_session(uid, SESSION_LOGN);
        if (session_tmp.get() == nullptr) // 创建session失败
        {
            return http_resp(ptr, false, websocketpp::http::status_code::internal_server_error, "创建session失败");
        }
        _session_ctl.set_session_remove(session_tmp->get_session_id(), TIME_OUT);

        // 在返回报文里面添加 cookie,将创建好的 session_id返回
        std::string session_str = "session_id=" + std::to_string(session_tmp->get_session_id());
        ptr->append_header("Set-Cookie", session_str);
        return http_resp(ptr, true, websocketpp::http::status_code::ok, "登录成功");
    }

    std::string get_value_from_cookie(const std::string &cookie_str, const std::string &k) // 从cookie中取出特定字段,这里取出session_id,没找到想要的字段就返回 " ";
    {
        // cookie中用 "; " 分割各个kv
        std::string esp = "; ";
        std::vector<std::string> v;

        string_util::str_cut(cookie_str, esp, v);
        esp = "=";
        for (auto &e : v)
        {
            std::vector<std::string> tmp;
            // 对每个取出的kv结构进行拆解，拿到特定 v
            string_util::str_cut(e, esp, tmp);
            if (tmp.size() != 2)
            {
                continue;
            }
            if (tmp[0] == k)
            {
                return tmp[1];
            }
        }
        return "";
    }

    // 获取用户信息
    void info(server::connection_ptr &ptr)
    {
        Json::Value json_ret;
        // 获取客户端json信息，查看是否有cookie字段来判断是否登录过，没有登录过则重新登录
        std::string cookie_value = ptr->get_request_header("Cookie"); //  拿到 cookie字段
        if (cookie_value.empty())
        { // 没有cookie就代表没有登录过,返回错误
            return http_resp(ptr, true, websocketpp::http::status_code::bad_request, "没有cookie，请重新登录");
        }

        // 有cookie则从cookie中取出session_id,判断session_id是否存在
        std::string session_val = get_value_from_cookie(cookie_value, "session_id");
        if (session_val.empty())
        {
            return http_resp(ptr, true, websocketpp::http::status_code::bad_request, "没有session_id，请重新登录");
        }
        int session_id = stoi(session_val);
        session_ptr s_ptr = _session_ctl.find_session(session_id);
        if (s_ptr.get() == nullptr)
        {
            return http_resp(ptr, true, websocketpp::http::status_code::bad_request, "没有session_id，请重新登录");
        }

        int uid = s_ptr->get_uid();
        // 从数据库里获取信息
        Json::Value user_info;
        if (!_table.select_by_id(uid, user_info))
        { // 获取失败
            return http_resp(ptr, true, websocketpp::http::status_code::bad_request, "信息获取失败");
        }
        std::string body;
        json_util::serialize(user_info, body);
        ptr->set_body(body);
        ptr->set_status(websocketpp::http::status_code::ok);
        ptr->append_header("Content-Type", "application/json");

        // 更新过期时间
        _session_ctl.set_session_remove(session_id, TIME_OUT);
    }

    // http返回函数
    void http_hander(websocketpp::connection_hdl hdl)
    {
        // 获取connect连接
        server::connection_ptr ptr = _ser.get_con_from_hdl(hdl);
        // 获取url和method
        websocketpp::http::parser::request res = ptr->get_request();

        std::string uri = res.get_uri();
        std::string method = res.get_method();

        // 获取正文
        std::string body = ptr->get_request_body();

        if (method == "POST" && uri == "/login") // 登录页面
        {
            user_login(ptr);
        }
        else if (method == "POST" && uri == "/reg") // 注册页面
        {
            user_reg(ptr);
        }
        else if (method == "GET" && uri == "/info")
        {
            info(ptr);
        }
        else // 静态资源请求
        {
            ret_file(ptr);
        }
    }

    void web_ret(server::connection_ptr &ptr, Json::Value &ret)
    {
        std::string body;
        json_util::serialize(ret, body);
        ptr->send(body);
        return;
    }
    session_ptr get_session_from_cookie(server::connection_ptr &ptr)
    {
        Json::Value json_ret;
        // 获取客户端json信息，查看是否有cookie字段来判断是否登录过，没有登录过则重新登录
        std::string cookie_value = ptr->get_request_header("Cookie"); //  拿到 cookie字段
        if (cookie_value.empty())
        { // 没有cookie就代表没有登录过,返回错误
            json_ret["optype"] = "hall_ready";
            json_ret["reason"] = "没有cookie";
            json_ret["result"] = false;
            web_ret(ptr, json_ret);
            return session_ptr();
        }

        // 有cookie则从cookie中取出session_id,判断session_id是否存在
        std::string session_val = get_value_from_cookie(cookie_value, "session_id");
        if (session_val.empty())
        {
            // 没有cookie就代表没有登录过,返回错误
            json_ret["optype"] = "hall_ready";
            json_ret["reason"] = "没有session_id";
            json_ret["result"] = false;
            web_ret(ptr, json_ret);
            return session_ptr();
        }
        int session_id = stoi(session_val);
        session_ptr s_ptr = _session_ctl.find_session(session_id);
        if (s_ptr.get() == nullptr)
        {
            json_ret["optype"] = "hall_ready";
            json_ret["reason"] = "session_id不存在";
            json_ret["result"] = false;
            web_ret(ptr, json_ret);
            return session_ptr();
        }
        return s_ptr;
    }

    void hall_handle(server::connection_ptr &ptr)
    { // 大厅连接处理函数
        // 1, 登录验证并拿到session对象
        Json::Value json_ret;
        session_ptr s_ptr = get_session_from_cookie(ptr);
        if (s_ptr.get() == nullptr)
        {
            return;
        }

        // 2, 验证是否重复登陆
        int uid = s_ptr->get_uid();
        if (_conn.find_room_id(uid) || _conn.find_square_id(uid))
        {
            json_ret["optype"] = "hall_ready";
            json_ret["reason"] = "重复登录";
            json_ret["result"] = false;
            web_ret(ptr, json_ret);
            return;
        }

        // 3,添加到在线用户管理模块
        _conn.add_square_id(uid, ptr);

        // 4，将sessio设置为永久
        _session_ctl.set_session_remove(s_ptr->get_session_id(), TIME_FOREVER);

        // 5，返回执行成功的应答
        json_ret["optype"] = "hall_ready";
        json_ret["reason"] = "执行成功";
        json_ret["result"] = true;
        web_ret(ptr, json_ret);
        return;
    }

    void room_handle(server::connection_ptr &ptr)
    {
        // 1,拿到session
        Json::Value json_ret;
        session_ptr s_ptr = get_session_from_cookie(ptr);
        if (s_ptr.get() == nullptr)
        {
            return;
        }

        // 2,验证是重复登录
        int uid = s_ptr->get_uid();
        if (_conn.find_room_id(uid) || _conn.find_square_id(uid))
        {
            json_ret["optype"] = "room_ready";
            json_ret["reason"] = "重复登录";
            json_ret["result"] = false;
            web_ret(ptr, json_ret);
            return;
        }

        // 3,查看房间是否创建成功
        room_ptr rptr = _room_ctl.find_rptr_by_uid(uid);
        if (rptr.get() == nullptr)
        {
            json_ret["optype"] = "room_ready";
            json_ret["reason"] = "玩家房间创建失败";
            json_ret["result"] = false;
            web_ret(ptr, json_ret);
            return;
        }

        // 4,加入到在线用户管理模块(room)
        _conn.add_room_id(uid, ptr);

        // 5,设置cookie时间为永久
        _session_ctl.set_session_remove(s_ptr->get_session_id(), TIME_FOREVER);

        // 6,构建回复
        json_ret["optype"] = "room_ready";
        json_ret["reason"] = "成功创建房间";
        json_ret["result"] = true;
        json_ret["room_id"] = rptr->get_id();
        json_ret["uid"] = s_ptr->get_uid();
        json_ret["white_id"] = rptr->get_white();
        json_ret["black_id"] = rptr->get_black();
        web_ret(ptr, json_ret);
        return;
    }
    // websock握手成功函数
    void web_open_hander(websocketpp::connection_hdl hdl)
    {
        server::connection_ptr ptr = _ser.get_con_from_hdl(hdl);
        websocketpp::http::parser::request res = ptr->get_request();
        Json::Value json_ret;

        // 执行不同的服务
        std::string uri = res.get_uri();
        if (uri == "/hall")
        {
            return hall_handle(ptr);
        }
        else if (uri == "/room")
        {
            return room_handle(ptr);
        }
    }

    void hall_close_handle(server::connection_ptr &ptr)
    { // 大厅连接关闭函数
        // 1, 登录验证并拿到session对象
        Json::Value json_ret;
        session_ptr s_ptr = get_session_from_cookie(ptr);
        if (s_ptr.get() == nullptr)
        {
            return;
        }
        int uid = s_ptr->get_uid();

        // 2,从在线用户中删除
        _conn.del_square_id(uid);

        // 3, 设置session的生命周期
        _session_ctl.set_session_remove(s_ptr->get_session_id(), TIME_OUT);
    }

    void room_close_handle(server::connection_ptr &ptr)
    { // 房间连接关闭函数
        // 1，拿到session客户端信息
        Json::Value json_ret;
        session_ptr s_ptr = get_session_from_cookie(ptr);
        if (s_ptr.get() == nullptr)
        {
            return;
        }
        int uid = s_ptr->get_uid();

        // 2, 从在线用户管理模块中删除
        _conn.del_room_id(uid);

        // 3, 调用用户退出函数，用户都退出后房间销毁
        _room_ctl.user_exit(uid);

        // 4，设置session时间为暂时
        _session_ctl.set_session_remove(s_ptr->get_session_id(), TIME_OUT);
    }
    // websock连接断开函数
    void web_close_hander(websocketpp::connection_hdl hdl)
    {
        server::connection_ptr ptr = _ser.get_con_from_hdl(hdl);
        websocketpp::http::parser::request res = ptr->get_request();
        Json::Value json_ret;

        // 执行不同的服务
        std::string uri = res.get_uri();
        if (uri == "/hall")
        {
            return hall_close_handle(ptr);
        }
        else if (uri == "/room")
        {
            return room_close_handle(ptr);
        }
    }

    void hall_run_handle(server::connection_ptr &ptr, server::message_ptr &mes)
    {
        // 1，登录验证并拿到session对象
        session_ptr s_ptr = get_session_from_cookie(ptr);
        if (s_ptr.get() == nullptr)
        {
            return;
        }
        int uid = s_ptr->get_uid();

        // 2，拿到数据本体
        std::string body_str = mes->get_payload();
        Json::Value body_json;
        json_util::unserialize(body_json, body_str);

        // 3, 判断报文格式是否正确,并且1通过行为类型执行不同函数
        Json::Value ret_json;
        if (!body_json["optype"].isNull() && body_json["optype"].asString() == "match_start")
        {
            // 加入到匹配队列中
            _match_ctl.add(uid);
            ret_json["result"] = true;
            ret_json["optype"] = "match_start";
            return web_ret(ptr, ret_json);
        }
        else if (!body_json["optype"].isNull() && body_json["optype"].asString() == "match_stop")
        {
            // 从匹配队列移除
            _match_ctl.del(uid);
            ret_json["result"] = true;
            ret_json["optype"] = "match_stop";
            return web_ret(ptr, ret_json);
        }
        // 未知操作构建返回对象
        ret_json["result"] = false;
        ret_json["optype"] = "unknow";
        web_ret(ptr, ret_json);
    }

    void room_run_handle(server::connection_ptr &ptr, server::message_ptr &mes)
    {
        Json::Value ret_json;
        // 1,登录验证并拿到session对象
        session_ptr s_ptr = get_session_from_cookie(ptr);
        if (s_ptr.get() == nullptr)
        {
            return;
        }
        int uid = s_ptr->get_uid();

        //2,获取房间对象
        room_ptr rptr = _room_ctl.find_rptr_by_uid(uid);
        if(rptr.get() == nullptr)
        {
            ret_json["optype"] = "unknow";
            ret_json["result"] = false;
            ret_json["reason"] = "没有找到房间信息";
            return web_ret(ptr,ret_json);
        }

        //3,拿到服务的请求报文
        std::string body_str = mes->get_payload();
        Json::Value body_json;
        json_util::unserialize(body_json,body_str);

        //4,对服务进行处理
        rptr->con_handle(body_json);
    }
    // websocke连接处理函数
    void web_run_hander(websocketpp::connection_hdl hdl, server::message_ptr mes)
    {
        server::connection_ptr ptr = _ser.get_con_from_hdl(hdl);
        websocketpp::http::parser::request res = ptr->get_request();
        Json::Value json_ret;

        // 执行不同的服务
        std::string uri = res.get_uri();
        if (uri == "/hall")
        {
            return hall_run_handle(ptr, mes);
        }
        else if (uri == "/room")
        {
            return room_run_handle(ptr, mes);
        }
    }
};

#endif